#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>

#include <list>
#include <string>

#include <boost/foreach.hpp>
#include <boost/typeof/typeof.hpp>

using namespace std;

void signal_handler(int sig);

const char *host_ip = "127.0.0.1";
const int   host_port = 6000;
bool b_is_running = true;

int main()
{
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1) {
        cerr << "create socket error" << endl;
        return 1;
    }

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(host_ip);
    server_addr.sin_port = htons(host_port);

    if (bind(server_socket, (const struct sockaddr*)&server_addr, 
                sizeof(server_addr)) == -1) {
        cerr << "bind error" << endl;
        close(server_socket);
        return 1;
    }
    cout << "binding at " << host_port << endl;

    listen(server_socket, 10);

    cout << "--Start--" << endl;

    signal(SIGKILL, signal_handler);

    typedef pair<int, string> DF_ITEM;
    typedef list<DF_ITEM> FD_LIST;
    FD_LIST fds_list;

    fds_list.push_back(make_pair(server_socket, "server"));

    while (b_is_running) {

        fd_set rfds;
        FD_ZERO(&rfds);
        int max_fds = 0;
        BOOST_FOREACH(const DF_ITEM &item, fds_list) {
            int this_fds = item.first;
            if (this_fds > max_fds)
                max_fds = this_fds;
            FD_SET(this_fds, &rfds);

        }

        const int buffer_size = 1024;
        char buffer[buffer_size] = {0};

        int select_ret = select(max_fds+1, &rfds, NULL, NULL, NULL);
        if (select_ret < 0) {
            cerr << "select error!" << endl;
        } else if (select_ret == 0) {
            cout << "select timeout." << endl;
        } else {
            //FD_LIST fds_list_copy = fds_list;
            BOOST_AUTO(fds_list_copy, fds_list);
            BOOST_FOREACH(const DF_ITEM &item, fds_list_copy) {
                int this_fds = item.first;
                if (FD_ISSET(this_fds, &rfds)) {
                    string this_name = item.second;
                    //! New client connect.
                    if (this_name == "server") {
                        struct sockaddr client_addr;
                        socklen_t client_addr_len;

                        int client_socket = accept(this_fds, &client_addr, &client_addr_len);
                        if (client_socket == -1) {
                            cerr << "accept error." << endl;
                        } else {
                            cout << "accept connect." << endl;
                            fds_list.push_back(make_pair(client_socket, string("client")));
                            string echo("Login Success!\n");
                            write(client_socket, echo.c_str(), echo.length());
                        }
                    } else {
                        int rsize = read(this_fds, buffer, buffer_size);
                        BOOST_FOREACH(const DF_ITEM &item, fds_list) {
                            if (item.second == "client" && item.first != this_fds) {
                                write(item.first, buffer, rsize);
                            }
                        }
                    }
                }
            }
        }
    }

    //! close all socket
    BOOST_FOREACH(const DF_ITEM &item, fds_list) {
        close(item.first);
    }

    cout << "--End--" << endl;
    return 0;
}

void signal_handler(int sig)
{
    b_is_running = false;    
    cout << "Ctrl+C" << endl;
}
